package com.wkq.jdbctemplate.commons;


import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.omg.SendingContext.RunTime;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * Created by yuj-b on 2016/5/27.
 */
public class Record extends LinkedHashMap<String, Object> {

    public static String NULL_STRING = "" ;

    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss" ;

    @JsonIgnore
    public Object get(String key) {
        return super.get(key);
    }

    public Long verifyAndGetId(){
        Long id  = id();
        verifyNull(id);
        return id;
    }

    @JsonIgnore
    public <T> T verifyAndGet(String key){
        Object value = get(key);
        verifyNull(value);
        return (T)value;
    }

    public void verifyNull(Object value){
        if(value ==  null){
            throw new RuntimeException("不可为空");
        }
    }

    @JsonIgnore
    public Object getScalar() {

        assert size()==1;

        for (Map.Entry<String, Object> entry : entrySet()) {
            return entry.getValue();
        }

        throw new RuntimeException("not a scalar record, values size :" + size());
    }

    @JsonIgnore
    public Integer getScalarInt() {
        Object scalar = getScalar();
        return scalar==null ? null : Integer.parseInt(scalar.toString());
    }

    @JsonIgnore
    public Long getScalarLong() {
        Object scalar = getScalar();
        return scalar==null ? null : Long.parseLong(scalar.toString());
    }

    @JsonIgnore
    public String getScalarString() {
        Object scalar = getScalar();
        return scalar==null ? NULL_STRING : scalar.toString();
    }

    @JsonIgnore
    public String getScalarString(String nullValue) {
        Object scalar = getScalar();
        return scalar==null ? nullValue : scalar.toString();
    }

    @JsonIgnore
    public Integer getInt(String key) {
        Object value = get(key);
        return value==null ? null : Integer.parseInt(value.toString());
    }

    @JsonIgnore
    public Long  getLong(String key) {
        Object value = get(key);
        return value==null ? null : Long.parseLong(value.toString());
    }

    @JsonIgnore
    public Boolean getBoolean(String key) {
        Object value = get(key);

        if (value==null) return null ;

        if (value instanceof Boolean) {
            return (Boolean)value ;
        } else if (value instanceof Integer) {
            return !value.equals(0);
        } else {
            throw new RuntimeException("can not cast object to boolean, type : " + value.getClass().getName());
        }
    }

    @JsonIgnore
    public Integer getInt(String key, int nullValue) {
        Integer value = getInt(key);
        return value==null ? nullValue : value;
    }

    @JsonIgnore
    public Long getLong(String key, long nullValue) {
        Long value = getLong(key);
        return value==null ? nullValue : value;
    }

    @JsonIgnore
    public Record getRecord(String key) {
        Object value = get(key);
        if (value==null) {
            return null ;
        } else if (value instanceof Map) {

            // 注意：每次返回实例不同
            Record record = new Record();
            record.putAll((Map)value);
            return record;

        } else {
            throw new RuntimeException("can not cast object to record, type : " + value.getClass().getName());
        }
    }

    @JsonIgnore
    public boolean isNull(String key) {
        return get(key)==null;
    }

    @JsonIgnore
    public List<Record> getList(String key) {
        Object value = get(key);
        if (value==null) {
            return null;
        } else if (value instanceof List) {

            List list = (List)value;
            if (list.size()>0) {

                if (list.get(0) instanceof Map) {
                    List<Record> newRecordList = new ArrayList<>();
                    for (Object record : list) {
                        newRecordList.add(new RecordBuilder().putAll((Map)record).build());
                    }

                    synchronized (this) {
                        list.clear();
                        list.addAll(newRecordList);
                    }

                    return list;

                } else {
                    throw new RuntimeException("can not handle item of type '" + list.get(0).getClass().toString() + "'");
                }

            } else {

                return list ;
            }

        } else {
            throw new RuntimeException("can not cast object to List, type : " + value.getClass().getName());
        }
    }

    @JsonIgnore
    public String getIntString(String key, ValueMapping mapping) {
        return mapping.get(getInt(key)).toString();
    }

    @JsonIgnore
    public Float getFloat(String key) {
        Object value = get(key);
        return value==null ? null : Float.parseFloat(value.toString());
    }

    @JsonIgnore
    public Float getFloat(String key, float nullValue) {
        Float value = getFloat(key);
        return value==null ? nullValue : value ;
    }

    @JsonIgnore
    public Long id() {
        return getLong("id");
    }

    @JsonIgnore
    public String getFloatString(String key, String nullValue, String format) {
        Float value = getFloat(key);
        return value==null ? nullValue : String.format(format, value);
    }

    @JsonIgnore
    public Double getDouble(String key) {
        Object value = get(key);
        return value==null ? null : Double.parseDouble(value.toString());
    }

    @JsonIgnore
    public Double getDouble(String key, double nullValue) {
        Double value = getDouble(key);
        return value==null ? nullValue : value;
    }

    @JsonIgnore
    public String getDoubleString(String key, String nullValue, String format) {
        Double value = getDouble(key);
        return value==null ? nullValue : String.format(format, value);
    }

    @JsonIgnore
    public String getString(String key) {
        Object value = get(key);
        return value==null ? null : (String)value;
    }

    @JsonIgnore
    public String getString(String key, ValueMapping mapping) {
        return mapping.get(getString(key)).toString();
    }

    @JsonIgnore
    public String getString(String key, String nullValue) {
        Object value = get(key);
        return value==null ? nullValue : (String)value;
    }

    @JsonIgnore
    public Date getDate(String key) {
        return getDate(key,"yyyy-MM-dd HH:mm:ss");
    }

    @JsonIgnore
    public Date getDate(String key,String format) {
        Object object = get(key);
        if (object==null) return null ;
        else if (object instanceof Date) return (Date)object;
        else if (object instanceof String) {

            if (object.toString().length()==0) return null ;

            try {
                return new SimpleDateFormat(format).parse(object.toString());
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
        else if (object instanceof Long) return new Date((Long)object);
        else {
            throw new RuntimeException("can not parse date, object:" + object.getClass().getName());
        }
    }

    @JsonIgnore
    public String getDateString(String key) {
        Date value = getDate(key);
        return value==null ? NULL_STRING : DateFormatUtils.format(value, DATE_FORMAT);
    }

    @JsonIgnore
    public String getDateString(String key, String format) {
        Date value = getDate(key);
        return value==null ? NULL_STRING : DateFormatUtils.format(value, format);
    }

    @JsonIgnore
    public String getDateString(String key, String format, String defaultValue) {
        Date value = getDate(key);
        return value==null ? defaultValue : DateFormatUtils.format(value, format);
    }

    private final Map<String, String> lowerCaseMap = new HashMap<String, String>();

    @Override
    public boolean containsKey(Object key) {
        Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
        return super.containsKey(realKey);
    }

    @Override
    @JsonIgnore
    public Object get(Object key) {
        Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
        return super.get(realKey);
    }

    @Override
    public Object put(String key, Object value) {
        Object oldKey = lowerCaseMap.put(key.toLowerCase(Locale.ENGLISH), key);
        Object oldValue = super.remove(oldKey);
        super.put(key, value);
        return oldValue;
    }

    @Override
    public void putAll(Map<? extends String, ?> m) {
        for (Map.Entry<? extends String, ?> entry : m.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            this.put(key, value);
        }
    }

    @Override
    public Object remove(Object key) {
        Object realKey = lowerCaseMap.remove(key.toString().toLowerCase(Locale.ENGLISH));
        return super.remove(realKey);
    }

    public void addInt(String key, Integer value) {
        if (value==null) return;

        Integer oldValue = getInt(key);
        if (oldValue==null) {
            put(key, value);
        } else {
            put(key, oldValue + value);
        }
    }

    public void addLong(String key, Long value) {
        if (value==null) return;

        Long oldValue = getLong(key);
        if (oldValue==null) {
            put(key, value);
        } else {
            put(key, oldValue + value);
        }
    }

    public Record removeKeys(String ...keys) {
        if (keys!=null) {
            for (String key : keys) {
                remove(key);
            }
        }

        return this ;
    }

    @JsonIgnore
    public String getKey(String[] keyFields, String seperate) {
        String key = "";
        for (int i = 0; i < keyFields.length; i++) {
            if (i == 0) {
                key += get(keyFields[i]);
            } else {
                key += seperate + get(keyFields[i]);
            }
        }
        return key;
    }
}
